 /* 
 *--------------------------------------------------------------------------
 *
 *      Serialflexer - Serial Class Library for Win32
 *      Copyright(C) 2008, Jiang Sanyi
 * 
 *      Contact Email: jiangsy@gmail.com
 * 
 * 
 *      This library is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU Lesser General Public
 *      License as published by the Free Software Foundation; either
 *      version 2 of the License, or (at your option) any later version.
 * 
 *      This library is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *      Lesser General Public License for more details.
 * 
 *      You should have received a copy of the GNU Lesser General Public
 *      License along with this library in the file COPYING.LIB;
 *      if not, write to the Free Software Foundation, Inc.,
 *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 *
 * --------------------------------------------------------------------------
 */
///
/// \brief: definitions for Serial Data Terminal Equipment
/// 
/// use template parameter of buffer length to make buffer more complexer
/// a message queue is used to implement serial writer thread
#pragma once


///One of the many factors to consider when choosing between nonoverlapped and 
///overlapped operations is portability. Overlapped operation is not a good choice 
///because most operating systems do not support it. 
///Most operating systems support some form of multithreading, however, 
///so multithreaded nonoverlapped I/O may be the best choice for portability reasons.

#define DEF_SERIAL_DATA_LENGTH 100

//typedef unsigned int  ( __stdcall * IFLEXR_THREAD_FUNC )( void * );
typedef void * ( __cdecl * IFLEXR_THREAD_FUNC )( void * );

//typedef enum DTE_PIN_E{
//	DTE_25_PIN=0,
//	DTE_9_PIN
//}dte_pin_e;

//template <int DTEMODEL = DTE_READ|DTE_WRITE|DTE_STATUS>
typedef enum DTE_MODEL_TYPE_E{
	DTE_READ = 0x01,
	DTE_WRITE = 0x02,
	DTE_STATUS = 0x04
}dte_monitor_type_e;

//
// GLOBAL DEFINES
//
#define MAX_WRITE_BUFFER        1024
#define MAX_READ_BUFFER         2048
#define READ_TIMEOUT            500
#define STATUS_CHECK_TIMEOUT    500
#define WRITE_CHECK_TIMEOUT     500
#define PURGE_FLAGS             PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR 
#define EVENTFLAGS_DEFAULT      EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD
#define FLAGCHAR_DEFAULT        '\n'

//
// ascii definitions
//
#define ASCII_BEL       0x07
#define ASCII_BS        0x08
#define ASCII_LF        0x0A
#define ASCII_CR        0x0D
#define ASCII_XON       0x11
#define ASCII_XOFF      0x13

//
// data structures
//
typedef struct CommInfoStruct
{
    HANDLE  hCommPort, hReaderStatus, hWriter ;
    DWORD   dwEventFlags;
    CHAR    chFlag, chXON, chXOFF;
    WORD    wXONLimit, wXOFFLimit;
    DWORD   fRtsControl;
    DWORD   fDtrControl;
    BOOL    fConnected, fTransferring, fRepeating,
            fCTSOutFlow, fDSROutFlow, fDSRInFlow, 
            fXonXoffOutFlow, fXonXoffInFlow,
            fTXafterXoffSent,
            fNoReading, fNoWriting, fNoEvents, fNoStatus,
            fDisplayTimeouts;
    BYTE    bPort, bByteSize, bParity, bStopBits ;
    DWORD   dwBaudRate ;
    COMMTIMEOUTS timeoutsorig;
    COMMTIMEOUTS timeoutsnew;
	CommInfoStruct()
	{
		InitCommInfo();
	}
	void InitCommInfo();
} CommInfo;

//
// macros ( for easier readability )
//
#define COMDEV( x )         ((x).hCommPort)
#define PORT( x )           ((x).bPort)
#define CONNECTED( x )      ((x).fConnected)
#define BYTESIZE( x )       ((x).bByteSize)
#define PARITY( x )         ((x).bParity)
#define STOPBITS( x )       ((x).bStopBits)
#define BAUDRATE( x )       ((x).dwBaudRate)
#define TIMEOUTSORIG( x )   ((x).timeoutsorig)
#define TIMEOUTSNEW( x )    ((x).timeoutsnew)
#define WRITERTHREAD( x )   ((x).hWriter)
#define READSTATTHREAD( x ) ((x).hReaderStatus)
#define EVENTFLAGS( x )     ((x).dwEventFlags)
#define FLAGCHAR( x )       ((x).chFlag)

#define DTRCONTROL( x )     ((x).fDtrControl)
#define RTSCONTROL( x )     ((x).fRtsControl)
#define XONCHAR( x )        ((x).chXON)
#define XOFFCHAR( x )       ((x).chXOFF)
#define XONLIMIT( x )       ((x).wXONLimit)
#define XOFFLIMIT( x )      ((x).wXOFFLimit)
#define CTSOUTFLOW( x )     ((x).fCTSOutFlow)
#define DSROUTFLOW( x )     ((x).fDSROutFlow)
#define DSRINFLOW( x )      ((x).fDSRInFlow)
#define XONXOFFOUTFLOW( x ) ((x).fXonXoffOutFlow)
#define XONXOFFINFLOW( x )  ((x).fXonXoffInFlow)
#define TXAFTERXOFFSENT(x)  ((x).fTXafterXoffSent)

#define NOREADING( x )      ((x).fNoReading)
#define NOWRITING( x )      ((x).fNoWriting)
#define NOEVENTS( x )       ((x).fNoEvents)
#define NOSTATUS( x )       ((x).fNoStatus)
#define SHOWTIMEOUTS( x )   ((x).fDisplayTimeouts)

///////////////////////////////////////////////////////////////////////////////
template<int DTEMODEL = DTE_READ|DTE_WRITE|DTE_STATUS, 
	int READBUFFERSIZE = MAX_READ_BUFFER,
	int WRITEBUFFERSIZE = MAX_WRITE_BUFFER>
class Serialflexer
	: public IDteCallBack
	//, public IDteStatusError
	, protected CommInfo
{
public:
	Serialflexer();
	~Serialflexer();

public:
	BOOL Open(int nIndex, LPCTSTR strPortDesc);
	void Close();
	BOOL UpdateConnection();//Currently not open other parameters in CommInfo for user setting
	const CommInfo& GetCommInfo(void) const{return *((CommInfo*)this);};
	BOOL Write(LPBYTE message, int length);

protected:
	int Start();	// will called by Open()
	void Stop();	// will called by Close()

protected:
	//
	// Error 
	// Status
	// Comm state
	//
	void CheckModemStatus( BOOL bUpdateNow);
	void CheckComStat( BOOL bUpdateNow);
	// the following ERROR&STATUS functions internally 
	// generate IDteCallBack invoke
	void ReportCommError( void );
	void ReportComStat( COMSTAT comStatus );
	void ReportModemStatus(DWORD dwModemStatus);
	void ReportStatusEvent(DWORD dwStatus);
	void ErrorReporter( DWORD dwErrorCode/*TCHAR * szMessage*/ ) ;
	void ErrorInComm( DWORD dwErrorCode/*TCHAR * szMessage*/ );

private:

	// todo: singleton thread
	class CDteThreadBase
	{
	protected:
		CDteThreadBase();//not allow to instantiate the base class since no threadproc here
	public:
		virtual ~CDteThreadBase();
		virtual BOOL Start(Serialflexer<DTEMODEL, READBUFFERSIZE, WRITEBUFFERSIZE>* pClient);
		virtual void Stop();

	public:
		HANDLE m_hStarted;
		HANDLE m_hExit;
		//HANDLE m_hThread;
		pthread_t m_tid;
		//pthread_attr_t m_tattr;
		bool m_bStarted;
		IFLEXR_THREAD_FUNC m_pThreadFunc;
	};

	///////////////////////////////////////////////////////////////////////////////
	class CDteReaderOverlapped
		: public CDteThreadBase
	{
	public:
		CDteReaderOverlapped();
		~CDteReaderOverlapped();
		//virtual BOOL Start(HANDLE hCommPort, IDteCallBack* CallBackObject);

		static void* __cdecl ThreadFunc(void*pvParam);/*serial thread*/

	};

	///////////////////////////////////////////////////////////////////////////////
	class CDteWriterOverlapped
		: public CDteThreadBase
	{
	public:
		CDteWriterOverlapped();
		~CDteWriterOverlapped();
		//virtual BOOL Start(HANDLE hCommPort, IDteCallBack* CallBackObject);

		int Write(LPBYTE message, int length);/*return bytes written*/

		static void* __cdecl ThreadFunc(void*pvParam);/*serial thread*/

#define MQ_PATH     "/dtewriteroverlapped.mq"
#define MQ_MAXMSG   100

	protected:
		static struct mq_attr _mqattr;
		static mqd_t _mqd;
		static CHAR _write_buf[WRITEBUFFERSIZE];
	};
	///////////////////////////////////////////////////////////////////////////////

	class CDteStatusOverlapped
		: public CDteThreadBase
	{
	public:
		CDteStatusOverlapped();
		~CDteStatusOverlapped();
		//virtual BOOL Start(HANDLE hCommPort, IDteCallBack* CallBackObject);

		static void* __cdecl ThreadFunc(void*pvParam);/*serial thread*/

		//BYTE m_bBuffer[WRITEBUFFERSIZE];
		//DWORD m_nBufferSize;
	};

protected:
	CDteReaderOverlapped _reader;
	CDteStatusOverlapped _status;
	CDteWriterOverlapped _writer;
};//remember the comma


///////////////////////////////////////////////////////////////////////////////
#include "dte.inl"
///////////////////////////////////////////////////////////////////////////////
